//
//  CJCRCConfig.swift
//  CJPack
//
//  Created by 陈警卫 on 2025/7/11.
//

import Foundation

public protocol CJCRCTypeProtocol {
    associatedtype Value: FixedWidthInteger & UnsignedInteger
    var config: CJCRCConfig<Value> { get }
}

extension CJCRC8Type: CJCRCTypeProtocol {
    public var config: CJCRCConfig<UInt8> {
        return CJCRCConfigTool.crc8Config(type: self)
    }
}

extension CJCRC16Type: CJCRCTypeProtocol {
    public var config: CJCRCConfig<UInt16> {
        return CJCRCConfigTool.crc16Config(type: self)
    }
}

extension CJCRC32Type: CJCRCTypeProtocol {
    public var config: CJCRCConfig<UInt32> {
        return CJCRCConfigTool.crc32Config(type: self)
    }
}


public struct CJCRCConfig<T> {
    var initCrc: T
    var polynom: T
    var xor: T
    var refIn: Bool
    var refOut: Bool
}

public class CJCRCConfigTool {
    
    public static func config<T: CJCRCTypeProtocol>(_ type: T) -> CJCRCConfig<T.Value> {
        return type.config
    }
    
    public static func crc8Config(type: CJCRC8Type) -> CJCRCConfig<UInt8> {
        let polynom: UInt8
        let initial: UInt8
        let xor: UInt8
        let refIn: Bool
        let refOut: Bool

        switch type {
        case .autosar, .opensafety:
            polynom = 0x2F
        case .bluetooth:
            polynom = 0xA7
        case .cdma2000, .lte, .wcdma:
            polynom = 0x9B
        case .darc:
            polynom = 0x39
        case .dvbS2:
            polynom = 0xD5
        case .gsmA, .hitag, .iCode, .mifareMad, .saeJ1850, .tech3250:
            polynom = 0x1D
        case .gsmB:
            polynom = 0x49
        case .i432_1, .rohc, .smbus:
            polynom = 0x07
        case .maximDow, .nrsc5:
            polynom = 0x31
        }

        switch type {
        case .autosar, .cdma2000, .hitag, .nrsc5, .rohc, .saeJ1850, .tech3250:
            initial = 0xFF
        case .bluetooth, .darc, .dvbS2, .gsmA, .gsmB, .i432_1, .lte, .maximDow, .opensafety, .smbus, .wcdma:
            initial = 0x00
        case .iCode:
            initial = 0xFD
        case .mifareMad:
            initial = 0xC7
        }

        switch type {
        case .autosar, .gsmB:
            xor = 0xFF
        case .i432_1:
            xor = 0x55
        default:
            xor = 0x00
        }

        let refTypes: [CJCRC8Type] = [
            .bluetooth, .darc, .maximDow, .rohc, .tech3250, .wcdma,
        ]
        refIn = refTypes.contains(type)
        refOut = refIn

        return CJCRCConfig(initCrc: initial, polynom: polynom, xor: xor, refIn: refIn, refOut: refOut)
    }

    
    public static func crc16Config(type: CJCRC16Type) -> CJCRCConfig<UInt16> {
        
        
        let polynom: UInt16
        switch type {
        case .arc, .cms, .dds110, .maximDow, .modbus, .umts, .usb:
            polynom = 0x8005
        case .cdma2000:
            polynom = 0xC867
        case .dectR, .dectX:
            polynom = 0x0589
        case .dnp, .en13757:
            polynom = 0x3D65
        case .genibus, .gsm,. ibm3740, .ibmSdlc, .isoIec144433A, .kermit, .mcrf4xx, .riello, .spiFujitsu, .tms37157, .xmodem:
            polynom = 0x1021
        case .lj1200:
            polynom = 0x6F63
        case .m17, .opensafetyA:
            polynom = 0x5935
        case .nrsc5:
            polynom = 0x080B
        case .opensafetyB:
            polynom = 0x755B
        case .profibus:
            polynom = 0x1DCF
        case .t10Dif:
            polynom = 0x8BB7
        case .teledisk:
            polynom = 0xA097
        }

        let initial: UInt16
        switch type {
        case .arc, .dectR, .dectX, .dnp, .en13757, .gsm, .kermit, .lj1200, .maximDow, .opensafetyA, .opensafetyB, .t10Dif, .teledisk, .umts, .xmodem:
            initial = 0x0000
        case .cdma2000, .cms, .genibus, .ibm3740, .ibmSdlc, .m17, .mcrf4xx, .modbus, .nrsc5, .profibus, .usb:
            initial = 0xFFFF
        case .dds110:
            initial = 0x800D
        case .isoIec144433A:
            initial = 0xC6C6
        case .riello:
            initial = 0xB2AA
        case .spiFujitsu:
            initial = 0x1D0F
        case .tms37157:
            initial = 0x89EC
        }

        let xor: UInt16
        switch type {
        case .dnp, .en13757, .genibus, .gsm, .ibmSdlc, .maximDow, .profibus, .usb:
            xor = 0xFFFF
        case .dectR:
            xor = 0x0001
        default:
            xor = 0x0000
        }
        
        let refIn: Bool = [
            .arc, .dnp, .ibmSdlc, .isoIec144433A, .kermit, .maximDow, .mcrf4xx, .modbus, .nrsc5, .riello, .tms37157, .usb
        ].contains(type)
        
        let refOut: Bool = refIn

        return CJCRCConfig(
            initCrc: initial,
            polynom: polynom,
            xor: xor,
            refIn: refIn,
            refOut: refOut
        )
    }
    
    public static func crc32Config(type: CJCRC32Type) -> CJCRCConfig<UInt32> {
        
        let polynom: UInt32
        switch type {
        case .aixm:
            polynom = 0x814141AB
        case .autosar:
            polynom = 0xF4ACFB13
        case .base91D:
            polynom = 0xA833982B
        case .bzip2, .cksum, .isoHdlc, .jamcrc, .mpeg2, .sata:
            polynom = 0x04C11DB7
        case .cdRomEdc:
            polynom = 0x8001801B
        case .iscsi:
            polynom = 0x1EDC6F41
        case .mef:
            polynom = 0x741B8CD7
        case .xfer:
            polynom = 0x000000AF
        }
        
        let initial: UInt32
        switch type {
        case .aixm, .cdRomEdc, .cksum, .xfer:
            initial = 0x00000000
        case .sata:
            initial = 0x52325032
        case .autosar, .base91D, .bzip2, .iscsi, .isoHdlc, .jamcrc, .mef, .mpeg2:
            initial = 0xFFFFFFFF
        }
        
        
        let xor: UInt32
        switch type {
        case .aixm, .cdRomEdc, .jamcrc, .mef, .mpeg2, .xfer:
            xor = 0x00000000
        case .autosar, .base91D, .bzip2, .cksum, .iscsi, .isoHdlc:
            xor = 0xFFFFFFFF
        default :
            xor = 0xFFFFFFFF
        }
        
        let refIn: Bool = [.autosar, .base91D, .cdRomEdc, .iscsi, .isoHdlc, .jamcrc, .mef].contains(type)
        let refOut: Bool = refIn

        return CJCRCConfig(
            initCrc: initial,
            polynom: polynom,
            xor: xor,
            refIn: refIn,
            refOut: refOut
        )
    }

}



